package net.sf.odinms.scripting.rhinoEngine;

import javax.script.*;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ImporterTopLevel;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.Function;
import java.io.Reader;
import java.lang.reflect.Method;

/**
 * ScriptEngine 包装器，实现 ScriptEngine 和 Invocable 接口
 */
public class RhinoScriptEngineWrapper implements ScriptEngine, Invocable {

    private final ScriptEngineFactory factory;
    private Bindings globalBindings = new SimpleBindings();
    private Scriptable currentScope; // 保存最近的作用域，用于函数调用

    public RhinoScriptEngineWrapper(ScriptEngineFactory factory) {
        this.factory = factory;
    }

    // 核心方法：使用 Rhino 原生 API 执行脚本
    private Object evalWithRhino(String script, ScriptContext context) throws ScriptException {
        Context rhinoContext = Context.enter();
        try {
            // 配置 Rhino 以启用 importPackage
            rhinoContext.setOptimizationLevel(-1);
            Scriptable scope = new ImporterTopLevel(rhinoContext);
            this.currentScope = scope; // 保存作用域用于后续调用

            // 将 ScriptContext 中的绑定变量导入到 Rhino 作用域
            if (context != null) {
                Bindings engineBindings = context.getBindings(ScriptContext.ENGINE_SCOPE);
                if (engineBindings != null) {
                    for (String key : engineBindings.keySet()) {
                        Object value = engineBindings.get(key);
                        Object jsValue = Context.javaToJS(value, scope);
                        scope.put(key, scope, jsValue);
                    }
                }
            }

            // 执行脚本
            return rhinoContext.evaluateString(scope, script, "<script>", 1, null);

        } catch (Exception e) {
            throw new ScriptException("Rhino execution failed: " + e.getMessage());
        } finally {
            Context.exit();
        }
    }

    // --- 实现 Invocable 接口的方法 ---

    @Override
    public Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException {
        return invokeMethod(null, name, args);
    }

    @Override
    public Object invokeMethod(Object thiz, String name, Object... args) throws ScriptException, NoSuchMethodException {
        if (currentScope == null) {
            throw new ScriptException("No script has been evaluated yet");
        }

        Context rhinoContext = Context.enter();
        try {
            // 重新应用当前的绑定到作用域
            Bindings engineBindings = getBindings(ScriptContext.ENGINE_SCOPE);
            if (engineBindings != null) {
                for (String key : engineBindings.keySet()) {
                    Object value = engineBindings.get(key);
                    Object jsValue = Context.javaToJS(value, currentScope);
                    currentScope.put(key, currentScope, jsValue);
                }
            }

            // 从作用域中获取函数
            Object obj = currentScope.get(name, currentScope);
            if (!(obj instanceof Function)) {
                throw new NoSuchMethodException("Function '" + name + "' not found");
            }

            Function func = (Function) obj;

            // 转换参数为 JavaScript 对象
            Object[] jsArgs = new Object[args.length];
            for (int i = 0; i < args.length; i++) {
                jsArgs[i] = Context.javaToJS(args[i], currentScope);
            }

            // 调用函数
            return func.call(rhinoContext, currentScope, currentScope, jsArgs);

        } catch (Exception e) {
            if (e instanceof NoSuchMethodException) {
                throw (NoSuchMethodException) e;
            }
            throw new ScriptException("Failed to invoke method: " + e.getMessage());
        } finally {
            Context.exit();
        }
    }

    // --- 之前已有的 ScriptEngine 接口方法（保持原样）---

    @Override
    public Object eval(String script, ScriptContext context) throws ScriptException {
        return evalWithRhino(script, context);
    }

    @Override
    public Object eval(Reader reader, ScriptContext context) throws ScriptException {
        // 读取 Reader 内容到字符串
        try {
            StringBuilder sb = new StringBuilder();
            char[] buffer = new char[1024];
            int bytesRead;
            while ((bytesRead = reader.read(buffer)) != -1) {
                sb.append(buffer, 0, bytesRead);
            }
            return evalWithRhino(sb.toString(), context);
        } catch (Exception e) {
            throw new ScriptException("Failed to read script: " + e.getMessage());
        }
    }

    @Override
    public Object eval(String script) throws ScriptException {
        return eval(script, getContext());
    }

    @Override
    public Object eval(Reader reader) throws ScriptException {
        return eval(reader, getContext());
    }

    @Override
    public Object eval(String script, Bindings bindings) throws ScriptException {
        ScriptContext context = getContext();
        context.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
        return eval(script, context);
    }

    @Override
    public Object eval(Reader reader, Bindings bindings) throws ScriptException {
        ScriptContext context = getContext();
        context.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
        return eval(reader, context);
    }

    @Override
    public void put(String key, Object value) {
        getBindings(ScriptContext.ENGINE_SCOPE).put(key, value);
    }

    @Override
    public Object get(String key) {
        return getBindings(ScriptContext.ENGINE_SCOPE).get(key);
    }

    @Override
    public Bindings getBindings(int scope) {
        if (scope == ScriptContext.ENGINE_SCOPE) {
            return globalBindings;
        }
        return new SimpleBindings();
    }

    @Override
    public void setBindings(Bindings bindings, int scope) {
        if (scope == ScriptContext.ENGINE_SCOPE) {
            this.globalBindings = bindings != null ? bindings : new SimpleBindings();
        }
    }

    @Override
    public ScriptContext getContext() {
        SimpleScriptContext context = new SimpleScriptContext();
        context.setBindings(globalBindings, ScriptContext.ENGINE_SCOPE);
        return context;
    }

    @Override
    public void setContext(ScriptContext context) {
        this.globalBindings = context.getBindings(ScriptContext.ENGINE_SCOPE);
    }

    @Override
    public ScriptEngineFactory getFactory() {
        return this.factory;
    }

    @Override
    public Bindings createBindings() {
        return new SimpleBindings();
    }

    // 在 RhinoScriptEngineWrapper 类中添加
    public void diagnoseScope() {
        if (currentScope == null) {
            System.out.println("Current scope is null - no script has been executed yet");
            return;
        }

        Context ctx = Context.enter();
        try {
            // 获取作用域中的所有属性
            Object[] ids = currentScope.getIds();
            System.out.println("Scope contains " + ids.length + " items:");

            for (Object id : ids) {
                if (id instanceof String) {
                    String name = (String) id;
                    Object value = currentScope.get(name, currentScope);
                    System.out.println("  " + name + " = " + value +
                            " (type: " + (value != null ? value.getClass().getName() : "null") + ")");

                    // 如果是函数，显示更多信息
                    if (value instanceof Function) {
                        Function func = (Function) value;
                        System.out.println("    ^^^ FUNCTION ^^^");
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("Diagnosis failed: " + e.getMessage());
        } finally {
            Context.exit();
        }
    }

    // 添加一个辅助方法用于外部调用
    public static void diagnoseScope(ScriptEngine engine) {
        if (engine instanceof RhinoScriptEngineWrapper) {
            ((RhinoScriptEngineWrapper) engine).diagnoseScope();
        } else {
            System.out.println("Not a RhinoScriptEngineWrapper, cannot diagnose");
        }
    }

    @Override
    public <T> T getInterface(Class<T> clazz) {
        return getInterface(null, clazz);
    }

    @Override
    public <T> T getInterface(Object thiz, Class<T> clazz) {
        if (currentScope == null) {
            return null;
        }

        if (!clazz.isInterface()) {
            throw new IllegalArgumentException("Class must be an interface: " + clazz.getName());
        }

        // 创建动态代理，将接口方法调用映射到JavaScript函数
        return clazz.cast(java.lang.reflect.Proxy.newProxyInstance(
                clazz.getClassLoader(),
                new Class<?>[] { clazz },
                new ScriptInterfaceInvocationHandler(thiz != null ? thiz : currentScope)
        ));
    }

    /**
     * 动态代理的调用处理器
     */
    private class ScriptInterfaceInvocationHandler implements java.lang.reflect.InvocationHandler {
        private final Object target;

        public ScriptInterfaceInvocationHandler(Object target) {
            this.target = target;
        }

        @Override
        public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
            String methodName = method.getName();

            Context rhinoContext = Context.enter();
            try {
                // 确保使用当前的作用域
                Scriptable scope = currentScope;

                // 重新应用所有当前的绑定到作用域
                Bindings engineBindings = getBindings(ScriptContext.ENGINE_SCOPE);
                if (engineBindings != null) {
                    for (String key : engineBindings.keySet()) {
                        Object value = engineBindings.get(key);
                        Object jsValue = Context.javaToJS(value, scope);
                        scope.put(key, scope, jsValue);
                    }
                }

                // 检查cm是否存在（诊断）
                Object cmValue = scope.get("cm", scope);
                // System.out.println("cm in scope: " + (cmValue != null ? "FOUND" : "NOT FOUND"));
                if (cmValue != null) {
                    // System.out.println("cm type: " + cmValue.getClass().getName());
                }

                // 从作用域中获取JavaScript函数
                Object jsFunction = scope.get(methodName, scope);

                if (!(jsFunction instanceof Function)) {
                    throw new NoSuchMethodException("JavaScript function '" + methodName + "' not found");
                }

                Function function = (Function) jsFunction;

                // 转换参数
                Object[] jsArgs = new Object[args != null ? args.length : 0];
                for (int i = 0; i < jsArgs.length; i++) {
                    jsArgs[i] = Context.javaToJS(args[i], scope);
                }

                // 调用JavaScript函数
                return function.call(rhinoContext, scope, scope, jsArgs);

            } catch (Exception e) {
                throw new RuntimeException("Failed to invoke JavaScript function '" + methodName + "'", e);
            } finally {
                Context.exit();
            }
        }
    }
}